////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//   Software       :                                                         //
//   Layer          : SRC                                                     //
//   Package        :                                                         //
//   Component      :                                                         //
//                                                                            //
//   File           : cserver.cxx                                             //
//   Author         : RSO ( Rafael Sotomayor Brûlé )                          //
//   EMail          : rsotomayor@savtec.cl                                    //
//   Type           : C++ Source                                              //
//   Usage          :                                                         //
//   Purpose        : gestión de usuario                                      //
//                                                                            //
//                                                                            //
//   who when        what                                                     //
//   ~~~ ~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//   RSO 09-10-2010  Creation                                                 //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////


#include <pthread.h>
#include <sys/time.h> 
#include <fcntl.h>

#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

#include "mylib/errlog.h"
#include "mylib/utilconf.h"
#include "mylib/TCPEchoServer.h"
#include "mylib/ServerSocket.h"  


#include "myconfig.h"
#include "myglobal.h"
#include "formmain.h"

#include "myserver.h"

extern    MyConfig    *config_g;
extern    FormMain    *myFormMain_g;
extern    MyGlobal     *global_g;


#include "cdatabase.h"
extern CDataBase *database_g;

// Structure of arguments to pass to client thread */
struct ThreadArgs
{
    int clntSock;                      // Socket descriptor for client
};


#include <stdio.h>  /* for perror() */
#include <stdlib.h> /* for exit() */
#include <sys/socket.h> /* for socket(), bind(), and connect() */
#include <arpa/inet.h>  /* for sockaddr_in and inet_ntoa() */
#include <string.h>     /* for memset() */

#define MAXPENDING 5    /* Maximum outstanding connection requests */


#include <vector>
#include <string>
#include <iterator>
#include <algorithm>
#include <sstream>

vector<string> explode(const string& str, const char& ch) {
    string next = "";
    vector<string> result;

    // For each character in the string
    for (string::const_iterator it = str.begin(); it != str.end(); it++) {
        // If we've hit the terminal character
        if (*it == ch) {
                // If we have some characters accumulated
                if (next.length() > 0) {
                        // Add them to the result vector
                        result.push_back(next);
                        next = "";
                }
        } else {
                // Accumulate the next character into the sequence
                next += *it;
        }
    }

    return result;
}



int MyServer::AcceptTCPConnection(int servSock)
{
    int clntSock;                    /* Socket descriptor for client */
    struct sockaddr_in echoClntAddr; /* Client address */
    unsigned int clntLen;            /* Length of client address data structure */

    /* Set the size of the in-out parameter */
    clntLen = sizeof(echoClntAddr);
    
    /* Wait for a client to connect */
    if ((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr, 
           &clntLen)) < 0)
        DieWithError((char *) "accept() failed");
    
    // clntSock is connected to a client! 
    
    errLog::LOGERR((errLog::TRACE,"Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr)));

    return clntSock;
}


void MyServer::DieWithError(char *errorMessage)
{
    perror(errorMessage);
    int counter = 0 ;
    while ( counter++ < 3 ) {
      errLog::LOGERR((errLog::INFO,(char *) "WAITING TO QUIT  [%d] \n",counter));
      sleep(1);
    }
    exit(EXIT_FAILURE);
}


int MyServer::CreateTCPServerSocket(unsigned short port)
{
    int sock;                        /* socket to create */
    struct sockaddr_in echoServAddr; /* Local address */

    /* Create socket for incoming connections */
    if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
        DieWithError((char *) "socket() failed");
      
    /* Construct local address structure */
    memset(&echoServAddr, 0, sizeof(echoServAddr));   /* Zero out structure */
    echoServAddr.sin_family = AF_INET;                /* Internet address family */
    echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
    echoServAddr.sin_port = htons(port);              /* Local port */

    /* Bind to the local address */
    if (bind(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0)
        DieWithError((char *) "bind() failed");

    /* Mark the socket so it will listen for incoming connections */
    if (listen(sock, MAXPENDING) < 0)
        DieWithError((char *)"listen() failed");

    return sock;
}


int MyServer::parseBuffer ( char *buffer_p,int size_p,  string &transaccion_p ) {
  xmlChar *key;
  xmlDocPtr doc;
  xmlNodePtr cur;
  int retcode = -1 ;
  doc  = xmlParseMemory(buffer_p,size_p);

  if ( doc == NULL ) {   
    errLog::LOGERR((errLog::WARN,"Document not parsed successfully. \n" )); 
    return -1;
  }   
  errLog::LOGERR((errLog::TRACE,"Parsing Successful\n")); 

  cur = xmlDocGetRootElement ( doc );

  if ( cur == NULL ) {   
    errLog::LOGERR((errLog::WARN,"empty document \n" )); 
    xmlFreeDoc ( doc );
    return -1;
  }   
  errLog::LOGERR((errLog::TRACE,"Got the root Node\n")); 
  if ( xmlStrcmp ( cur->name, ( const xmlChar * ) "data" ) ) {
    errLog::LOGERR((errLog::WARN,"Document of the wrong type root node != \n")); 
    xmlFreeDoc(doc);
    return -1;
  }
  errLog::LOGERR((errLog::TRACE,"Got the root \n")); 

  cur = cur -> xmlChildrenNode;
  while ( cur != NULL ) {

    if ( ( !xmlStrcmp ( cur -> name, ( const xmlChar * ) "transaccion" ) ) )  {   
      key = xmlNodeListGetString ( doc, cur -> xmlChildrenNode,1);
      transaccion_p = (char *) key ;
      xmlFree ( key );
      retcode = 0 ;
    } 

    cur = cur -> next;
  }
  xmlFreeDoc ( doc );
  return retcode ;
}


void MyServer::MyHandleTCPClient(int clntSocket)
{
     char echoBuffer[RCVBUFSIZE];        // Buffer for echo string
    int recvMsgSize;                    // Size of received message
    // Receive message from client
    if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0) {
        // DieWithError((char *) "recv() failed");
      errLog::LOGERR((errLog::SEVERE,"recv() failed"));
      return ;
    }


    string messageIn;
    string mydata ;

    char  buffer[RCVBUFSIZE];

    string transaccion; 
    std::size_t foundBegin;
    std::size_t foundEnd;
    string strRetcode,strMsgRetval;

    if (recvMsgSize > 0) {     // zero indicates end of transmission

      // Echo message back to client 
//      printf("Socket %d   Rx=%d\n",clntSocket,recvMsgSize);

//      if (send(clntSocket, echoBuffer, recvMsgSize, 0) != recvMsgSize)
//          DieWithError((char *) "send() failed");

      // See if there is more data to receive
//      if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0) {
//           DieWithError((char *) "recv() failed");
//        errLog::LOGERR((errLog::SEVERE,"recv() failed"));
//        break;
//      }

      memcpy(&buffer[0],&echoBuffer[0],recvMsgSize);
      buffer[recvMsgSize] = '\0' ;     
      mydata    = buffer ;

      foundEnd=mydata.find("</data>");
      if (foundEnd!=std::string::npos) {
        string mydata_old = mydata.substr(foundEnd+7);
//        std::cout << "'END' also found at: " << foundEnd << '\n';
        foundBegin=mydata.find("<?xml");
        if (foundBegin !=std::string::npos) {
//          std::cout << "'BEGIN' also found at: " << foundBegin << '\n';
          mydata = mydata.substr(foundBegin);
        } 


        if ( parseBuffer ((char *) mydata.data(),mydata.length(), transaccion) == 0 ) {
          if ( procesaTransaccion(transaccion,strMsgRetval) == 0 ) {
            strRetcode  = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
            strRetcode += "<data>" ;
            strRetcode += "<response>OK</response>" ;
            strRetcode += strMsgRetval ;
            strRetcode += "</data>" ;
          } else {
            strRetcode  = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
            strRetcode += "<data>" ;
            strRetcode += "<response>KO</response>" ;
            strRetcode += strMsgRetval ;
            strRetcode += "<descripcion>No se pudo procesar</descripcion>" ;
            strRetcode += "</data>" ;
          }
        } else {
            strRetcode  = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
            strRetcode += "<data>" ;
            strRetcode += "<response>KO</response>" ;
            strRetcode += strMsgRetval ;
            strRetcode += "<descripcion>No se pudo procesar</descripcion>" ;
            strRetcode += "</data>" ;
        }

        if (send(clntSocket, strRetcode.data(), strRetcode.length(), 0) != (int) strRetcode.length()) {
          errLog::LOGERR((errLog::SEVERE,"send() failed"));
        }

        mydata = mydata_old ;
      }

    }

    errLog::LOGERR((errLog::INFO,(char *)"Bye socket %d",clntSocket)); 
    close(clntSocket);    /* Close client socket */
}


void *MyServer::ThreadMain(void *threadArgs)
{
    int clntSock;                   /* Socket descriptor for client connection */

    /* Guarantees that thread resources are deallocated upon return */
    pthread_detach(pthread_self()); 

    /* Extract socket file descriptor from argument */
    clntSock = ((struct ThreadArgs *) threadArgs) -> clntSock;
    free(threadArgs);              /* Deallocate memory for argument */

    pMyServer_a->MyHandleTCPClient(clntSock);

    return (NULL);
}

MyServer* MyServer::pMyServer_a=NULL;
  
MyServer::MyServer(int socket_p) {
  socket_a = socket_p;
  pMyServer_a = this;
}

MyServer::~MyServer() {
  
}


void *MyServer::listenTask(void *p) {
  
    int servSock;                    /* Socket descriptor for server */
    int clntSock;                    /* Socket descriptor for client */
    unsigned short echoServPort;     /* Server port */
    pthread_t threadID;              /* Thread ID from pthread_create() */
    struct ThreadArgs *threadArgs;   /* Pointer to argument structure for thread */

    echoServPort = pMyServer_a->socket_a;  /* First arg:  local port */

    servSock = pMyServer_a->CreateTCPServerSocket(echoServPort);

    while ( global_g->isOn_a ) {  // run forever
      clntSock = pMyServer_a->AcceptTCPConnection(servSock);

      // Create separate memory for client argument 
      if ((threadArgs = (struct ThreadArgs *) malloc(sizeof(struct ThreadArgs))) == NULL) {
        pMyServer_a->DieWithError((char *)"malloc() failed");
      }
      threadArgs -> clntSock = clntSock;
    // Create client thread
      if (pthread_create(&threadID, NULL, ThreadMain, (void *) threadArgs) != 0) {
        pMyServer_a->DieWithError((char *)"pthread_create() failed");
      }
    }
    
    return NULL;
  
}

void MyServer::startTareas() {
  
  int s;
  pthread_t threadListen;        // Thread ID para pthread_create() 
  pthread_attr_t atributos;
  pthread_attr_t *attrp;     
  attrp = &atributos;      
        
  if (pthread_attr_init(&atributos) != 0) {
    errLog::LOGERR((errLog::SEVERE,(char *)"Thread ID [threadArgsLed]:En creación de estructura de atributos.\n"));
    exit(EXIT_FAILURE);
  }
  if (pthread_attr_setdetachstate(&atributos,  PTHREAD_CREATE_DETACHED) != 0) {
    errLog::LOGERR((errLog::SEVERE,(char *)"Thread ID [threadArgsLed]:En el cambio de atributos.\n"));                  
    exit(EXIT_FAILURE);
  }  


  if ( (pthread_create(&threadListen, &atributos,listenTask, NULL)) != 0 ) {
    errLog::LOGERR((errLog::SEVERE,(char *)"Error creando Thread [ThreadPoll_a]\n")); 
    exit(EXIT_FAILURE);       
  }

  if (attrp != NULL) {
    s = pthread_attr_destroy(attrp);
    if (s != 0) {
      errLog::LOGERR((errLog::SEVERE,(char *)"Error [pthread_attr_destroy]\n")); 
    }
  }  


}

int MyServer::procesaTransaccion(string transaccion_p,string &message_p) {
//    printf("%s\n",mydata.data());
  int retcode = -1 ;
  //std::cout << messageIn ;
  errLog::LOGERR((errLog::INFO,(char *)"Procesando Transaccion [%s]\n",transaccion_p.data())); 
  gdk_threads_enter();   

  gdk_threads_leave();
  fflush(stdout);
//  string id;

  return retcode ;
}



